/**
 * 
 */
package neptune.manager.model.topology;

import java.util.ArrayList;

/**
 * @author Roberto Bifulco [info@robertobifulco.it, robertobifulco.it]
 * @author Luigi Lorenzo Arpino [luigiarpino@yahoo.it]
 * 
 */
public class Topology {

	/**
	 * Il nome della topologia
	 */
	private String name;
	/**
	 * La descrizione della topologia
	 */
	private String description;
	/**
	 * I nodi della topologia
	 */
	private ArrayList<Node> nodes = new ArrayList<Node>(0);
	/**
	 * I link della topologia
	 */
	private ArrayList<Link> links = new ArrayList<Link>(0);
	private boolean isValid = true;

	/**
	 * Aggiunge un nodo alla topologia
	 * 
	 * @param node
	 * @throws DuplicatedNodeException
	 */
	public void addNode(Node node) throws DuplicatedNodeException {
		// TODO eliminato il controllo sulla duplicazione poiche' e' spostato
		// nel validator
		// if (nodes.contains(node))
		// throw new DuplicatedNodeException("The node id '" + node.getId()
		// + "' is duplicated: the id must be unique for a node!");
		nodes.add(node);
	}

	/**
	 * Aggiunge un link alla topologia, perche' il link sia effettivamente
	 * aggiunto, � necessario prima aggiungere alla topologia i nodi che il link
	 * connette.<br/>
	 * <br/>
	 * 
	 * I controlli fatti dal metodo sono i seguenti:<br/>
	 * Esiste soltanto uno fra i nodi della topologia che ha fra le sua
	 * interfacce una con end-point del link<br/>
	 * Esiste soltanto uno fra i nodi della topologia che ha fra le sua
	 * interfacce una con end-point del link<br/>
	 * End-point sorgente e End-point destinazione del link non sono uguali.<br/>
	 * <br/>
	 * 
	 * In caso di IP Aliasing come tecnica di Link Multiplexing, questo metodo
	 * non consente che un link parta e arrivi nella stessa interfaccia di rete,
	 * anche se indirizzo IP sorgente e destinazione sono diversi.
	 * 
	 * @param link
	 * @throws LinkException
	 */
	public void addLink(Link link) throws LinkException {

		EndPoint source = link.getSource();
		EndPoint destination = link.getDestination();

		if (source == null || destination == null)
			throw new LinkException("Link '" + link.getId()
					+ "' source or destination end point is not defined.");

		// TODO inserire controlli per gli end point?
		// if (source instanceof OLPIEndPoint
		// && destination instanceof OLPIEndPoint)
		// verifyOLPILink(link);
		links.add(link);
	}

	// private void verifyOLPILink(Link link) throws LinkException {
	// OLPIEndPoint source = (OLPIEndPoint) link.getSource();
	// OLPIEndPoint destination = (OLPIEndPoint) link.getDestination();
	//
	// // Verifica la presenza di altri link sugli stessi EndPoint
	// for (Link definedLink : links) {
	// if (definedLink.getSource().equals(source))
	// throw new LinkException(
	// "Link '"
	// + link
	// + "' error: The source end point is equal to the link '"
	// + definedLink + "' source end point");
	// if (definedLink.getDestination().equals(source))
	// throw new LinkException(
	// "Link '"
	// + link
	// + "' error: The source end point is equal to the link '"
	// + definedLink + "' destination end point");
	// if (definedLink.getSource().equals(destination))
	// throw new LinkException(
	// "Link '"
	// + link
	// + "' error: The destination end point is equal to the link '"
	// + definedLink + "' source end point");
	// if (definedLink.getDestination().equals(destination))
	// throw new LinkException(
	// "Link '"
	// + link
	// + "' error: The destination end point is equal to the link '"
	// + definedLink + "' destination end point");
	// }
	//
	// // Verifica che End Point di partenza e arrivo siano diversi
	// if (source.equals(destination))
	// throw new LinkException(
	// "Link '"
	// + link
	// + "' source end point and destination end point are the same.");
	//
	// // Verifica l'esistenza dei nodi su cui sono definiti gli end point
	// Node sourceNode = searchNodeIntoTopology(source.getNodeId());
	// if (sourceNode == null)
	// throw new LinkException("The node '" + source.getNodeId()
	// + "' is not defined on this topology.");
	// Node destinationNode = searchNodeIntoTopology(destination.getNodeId());
	// if (destinationNode == null)
	// throw new LinkException("The node '" + destination.getNodeId()
	// + "' is not defined on this topology.");
	//
	// // Verifica l'esistenza delle interfacce su cui sono definiti gli end
	// // point
	// boolean sourceExists = false;
	// for (NetworkInterface netIf : sourceNode.getInterfaces()) {
	// if (source.isThisEndPoint(netIf))
	// sourceExists = true;
	// }
	// if (!sourceExists)
	// throw new LinkException("Link '" + link
	// + "' source end point interface doesn't exists.");
	//
	// boolean destinationExists = false;
	// for (NetworkInterface netIf : destinationNode.getInterfaces()) {
	// if (destination.isThisEndPoint(netIf))
	// destinationExists = true;
	// }
	// if (!destinationExists)
	// throw new LinkException("Link '" + link
	// + "' destination end point interface doesn't exists.");
	// }

	/**
	 * Restituisce un array contenente tutti i nodi definiti, o null se non ci
	 * sono nodi
	 * 
	 * @return the nodes
	 */
	public Node[] getNodes() {
		return (Node[]) nodes.toArray(new Node[0]);
	}

	/**
	 * Restituisce un array contenente tutti i link definiti o null se non ci
	 * sono link.
	 * 
	 * @return the links
	 */
	public Link[] getLinks() {
		return (Link[]) links.toArray(new Link[0]);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Cerca un nodo fra tutti i nodi definiti in questa topologia. Restituisce
	 * null se il nodo non viene trovato.<br>
	 * <br>
	 * E' importante notare che non ha importanza se un nodo e' definito come
	 * macchina virtuale su di un altro nodo, poiche' l'albero dei nodi e'
	 * gestito in modo "piatto" dalla classe Topology. In altre parole, la
	 * Topology conserve una lista dei nodi, ciascun nodo indica poi quale altro
	 * nodo e' suo figlio. Il sistema deve garantire che non ci siano figli
	 * condivisi/genitori multipli.
	 * 
	 * @param nodeId
	 * @return
	 */
	public Node searchNodeIntoTopology(String nodeId) {
		for (Node node : nodes) {
			if (node.getId().equals(nodeId))
				return node;
		}

		return null;
	}

	/**
	 * @return the isValid
	 */
	public boolean isValid() {
		return isValid;
	}

	/**
	 * @param isValid
	 *            the isValid to set
	 */
	public void setValid(boolean isValid) {
		this.isValid = isValid;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}
}
